import {
    Task,
    TaskEvent,
    TaskEventListener,
    TaskEventType,
    TaskFunction,
    TaskOptions,
    TaskPriority,
    TaskResult,
    TaskStatus,
} from './types';

// 任务执行器 - 负责任务的执行和队列管理
export interface TaskExecutorConfig {
    /**
     * 最大并发任务数
     * 默认值：1
     */
    concurrency: number;
    /**
     * 默认任务超时时间（毫秒）
     * 默认值：30000
     */
    defaultTimeout: number;
    /**
     * 最大重试次数
     * 默认值：3
     */
    maxRetries: number;
    /**
     * 重试延迟（毫秒）
     * 默认值：1000
     */
    retryDelay: number;
    /**
     * 是否启用空闲回调
     * 默认值：true
     */
    enableIdleCallback: boolean;
}

export class TaskExecutor {
    private readonly config: TaskExecutorConfig;
    private readonly pendingQueue: Task[] = [];
    private readonly runningTasks: Map<string, Task> = new Map();
    private readonly eventListeners: Map<TaskEventType, Set<TaskEventListener>> = new Map();
    private taskIdCounter = 0;

    constructor(config: Partial<TaskExecutorConfig> = {}) {
        this.config = {
            concurrency: 1,
            defaultTimeout: 30000,
            maxRetries: 3,
            retryDelay: 1000,
            enableIdleCallback: true,
            ...config,
        };

        this.initializeEventListeners();
    }

    /**
     * 提交任务到执行器
     */
    public submitTask<T = any>(
        name: string,
        taskFunction: TaskFunction<T>,
        options: TaskOptions = {}
    ): string {
        const task = this.createTask<T>(name, taskFunction, options);
        this.insertTaskByPriority(task);
        this.processQueue();
        return task.id;
    }

    /**
     * 批量提交任务
     */
    public submitBatchTasks(
        tasks: Array<{
            name: string;
            taskFunction: TaskFunction;
            options?: TaskOptions;
        }>
    ): string[] {
        const taskIds = tasks.map(({ name, taskFunction, options }) => {
            const task = this.createTask(name, taskFunction, options || {});
            this.insertTaskByPriority(task);
            return task.id;
        });

        if (this.isStarted) {
            this.processQueue();
        }
        return taskIds;
    }

    /**
     * 取消任务
     */
    public cancelTask(taskId: string): void {
        // 取消等待中的任务
        const pendingIndex = this.pendingQueue.findIndex((task) => task.id === taskId);
        if (pendingIndex !== -1) {
            const task = this.pendingQueue.splice(pendingIndex, 1)[0];
            if (task) {
                task.status = TaskStatus.CANCELLED;
                this.emitEvent({
                    type: 'taskCancelled',
                    task,
                    timestamp: Date.now(),
                });
            }
        }
    }

    /**
     * 取消所有同名任务
     */
    public cancelTasksByName(taskName: string): void {
        // 取消等待中的同名任务
        for (let i = this.pendingQueue.length - 1; i >= 0; i--) {
            const task = this.pendingQueue[i];
            if (task.name === taskName) {
                this.pendingQueue.splice(i, 1);
                task.status = TaskStatus.CANCELLED;

                this.emitEvent({
                    type: 'taskCancelled',
                    task,
                    timestamp: Date.now(),
                });
            }
        }
    }

    /**
     * 将多个任务提升到队列最前端（按指定顺序）
     */
    public promoteTasksToFront(taskIds: string[]): void {
        const promotedTaskIds: string[] = [];
        const tasksToPromote: Task[] = [];
        // 收集要提升的任务
        taskIds.forEach((taskId) => {
            const taskIndex = this.pendingQueue.findIndex((task) => task.id === taskId);
            if (taskIndex !== -1) {
                const task = this.pendingQueue.splice(taskIndex, 1)[0];
                tasksToPromote.push(task);
                promotedTaskIds.push(taskId);
            }
        });

        // 按照传入的顺序，将任务依次插入到队列最前端
        // 注意：为了保持顺序，需要从后往前插入
        for (let i = tasksToPromote.length - 1; i >= 0; i--) {
            this.pendingQueue.unshift(tasksToPromote[i]);
        }
        this.processQueue();
    }

    public promoteTasksToFrontByName(taskNames: string[]): void {
        const tasksToPromote: Task[] = [];
        // 收集要提升的任务
        taskNames.forEach((taskName) => {
            this.pendingQueue.forEach((task, index) => {
                if (task.name === taskName) {
                    tasksToPromote.push(task);
                    this.pendingQueue.splice(index, 1);
                }
            });
        });

        // 按照传入的顺序，将任务依次插入到队列最前端
        // 注意：为了保持顺序，需要从后往前插入
        for (let i = tasksToPromote.length - 1; i >= 0; i--) {
            this.pendingQueue.unshift(tasksToPromote[i]);
        }
        this.processQueue();
    }

    /**
     * 清空队列
     */
    public clearQueue(): void {
        // 取消所有等待中的任务
        this.pendingQueue.forEach((task) => {
            task.status = TaskStatus.CANCELLED;

            this.emitEvent({
                type: 'taskCancelled',
                task,
                timestamp: Date.now(),
            });
        });
        this.pendingQueue.length = 0;

        // 取消所有运行中的任务
        this.runningTasks.forEach((task) => {
            task.status = TaskStatus.CANCELLED;

            this.emitEvent({
                type: 'taskCancelled',
                task,
                timestamp: Date.now(),
            });
        });
        this.runningTasks.clear();
    }

    /**
     * 立即执行任务
     * @param taskId
     */
    public executeImmediately(taskId: string): void {
        const taskIndex = this.pendingQueue.findIndex((task) => task.id === taskId);
        if (taskIndex === -1) {
            console.warn(`Task with ID ${taskId} not found in pending queue`);
            return;
        }
        if (this.isStarted && !this.isPaused) {
            const task = this.pendingQueue.splice(taskIndex, 1)[0];
            if (task) {
                this.executeTask(task);
            }
        }
    }

    /**
     * 暂停/恢复队列处理
     */
    private isPaused = false;
    private isStarted = false;

    public pause(): void {
        this.isPaused = true;
    }

    public resume(): void {
        this.isPaused = false;
        this.processQueue();
    }

    /**
     * 启动队列处理
     * 只有在调用此方法后，队列才会开始处理任务
     */
    public start(): void {
        if (this.isStarted) {
            console.warn('TaskExecutor is already started');
            return;
        }

        this.isStarted = true;
        this.isPaused = false;

        this.emitEvent({
            type: 'queueStarted',
            task: this.pendingQueue[0],
            timestamp: Date.now(),
        });

        this.processQueue();
    }

    /**
     * 获取当前队列状态
     */
    public getQueueSnapshot(): {
        pending: ReadonlyArray<Readonly<Task>>;
        running: ReadonlyArray<Readonly<Task>>;
    } {
        return {
            pending: this.pendingQueue.map((task) => ({ ...task })),
            running: Array.from(this.runningTasks.values()).map((task) => ({ ...task })),
        };
    }

    /**
     * 添加事件监听器
     */
    public addEventListener(type: TaskEventType, listener: TaskEventListener): void {
        const listeners = this.eventListeners.get(type);
        if (listeners) {
            listeners.add(listener);
        }
    }

    /**
     * 移除事件监听器
     */
    public removeEventListener(type: TaskEventType, listener: TaskEventListener): void {
        const listeners = this.eventListeners.get(type);
        if (listeners) {
            listeners.delete(listener);
        }
    }

    /**
     * 销毁执行器
     */
    public destroy(): void {
        this.clearQueue();
        this.eventListeners.clear();
    }

    private createTask<T>(
        name: string,
        taskFunction: TaskFunction<T>,
        options: TaskOptions
    ): Task<T> {
        return {
            id: this.generateTaskId(),
            name,
            taskFunction,
            priority: options.priority ?? TaskPriority.NORMAL,
            createdAt: Date.now(),
            options: {
                priority: options.priority ?? TaskPriority.NORMAL,
                timeout: options.timeout ?? this.config.defaultTimeout,
                retryCount: options.retryCount ?? this.config.maxRetries,
                retryDelay: options.retryDelay ?? this.config.retryDelay,
            },
            status: TaskStatus.PENDING,
            retryAttempts: 0,
        };
    }

    private insertTaskByPriority(newTask: Task): void {
        const index = this.pendingQueue.findIndex((task) => task.priority < newTask.priority);

        if (index === -1) {
            this.pendingQueue.push(newTask);
        } else {
            this.pendingQueue.splice(index, 0, newTask);
        }
    }

    private async processQueue(): Promise<void> {
        if (this.isPaused || !this.isStarted) return;

        while (this.runningTasks.size < this.config.concurrency && this.pendingQueue.length > 0) {
            const task = this.pendingQueue.shift();
            if (task) {
                this.executeTask(task);
            }
        }
    }

    private async executeTask(task: Task): Promise<void> {
        this.runningTasks.set(task.id, task);
        task.status = TaskStatus.RUNNING;
        task.startedAt = Date.now();

        this.emitEvent({
            type: 'taskStarted',
            task,
            timestamp: Date.now(),
        });

        try {
            if (this.config.enableIdleCallback) {
                await this.waitForIdleTime();
            }

            const data = await this.performTask(task);
            await this.handleTaskSuccess(task, data);
        } catch (error) {
            await this.handleTaskError(task, error as Error);
        } finally {
            if (this.pendingQueue.length === 0 && this.runningTasks.size === 0) {
                this.emitEvent({
                    type: 'queueEmpty',
                    task,
                    timestamp: Date.now(),
                });
            }
        }
    }

    private async performTask(task: Task): Promise<any> {
        const timeoutPromise = new Promise((_, reject) => {
            setTimeout(() => {
                reject(new Error(`Task "${task.name}" timed out after ${task.options.timeout}ms`));
            }, task.options.timeout);
        });

        const taskPromise = task.taskFunction();
        return Promise.race([taskPromise, timeoutPromise]);
    }

    private async handleTaskSuccess(task: Task, data: any): Promise<void> {
        const completedAt = Date.now();
        const executionTime = completedAt - (task.startedAt ?? task.createdAt);

        task.status = TaskStatus.COMPLETED;
        task.completedAt = completedAt;

        const result: TaskResult = {
            id: task.id,
            name: task.name,
            data,
            executionTime,
            timestamp: completedAt,
        };

        this.runningTasks.delete(task.id);

        this.emitEvent({
            type: 'taskCompleted',
            task,
            result,
            timestamp: completedAt,
        });

        await this.processQueue();
    }

    private async handleTaskError(task: Task, error: Error): Promise<void> {
        task.error = error;
        task.retryAttempts++;

        if (task.retryAttempts < task.options.retryCount) {
            setTimeout(() => {
                task.status = TaskStatus.PENDING;
                this.runningTasks.delete(task.id);
                this.insertTaskByPriority(task);
                this.processQueue();
            }, task.options.retryDelay);
            return;
        }

        task.status = TaskStatus.FAILED;
        task.completedAt = Date.now();
        this.runningTasks.delete(task.id);

        this.emitEvent({
            type: 'taskFailed',
            task,
            timestamp: Date.now(),
        });

        await this.processQueue();
    }

    private waitForIdleTime(): Promise<void> {
        return new Promise((resolve) => {
            if ('requestIdleCallback' in window) {
                (window as any).requestIdleCallback(() => resolve(), { timeout: 1000 });
            } else {
                setTimeout(resolve, 0);
            }
        });
    }

    private generateTaskId(): string {
        return `task_${Date.now()}_${++this.taskIdCounter}`;
    }

    private initializeEventListeners(): void {
        const eventTypes: TaskEventType[] = [
            'taskStarted',
            'taskCompleted',
            'taskFailed',
            'taskCancelled',
            'queueEmpty',
            'queueStarted',
        ];
        eventTypes.forEach((type) => {
            this.eventListeners.set(type, new Set());
        });
    }

    private emitEvent(event: TaskEvent): void {
        const listeners = this.eventListeners.get(event.type);
        if (listeners) {
            listeners.forEach((listener) => {
                try {
                    listener(event);
                } catch (error) {
                    console.error(`Event listener error for ${event.type}:`, error);
                }
            });
        }
    }
}
